#include "pch.h"
#include "Renderer.h"
#include "Camera.h"
#include "RenderViewPort.h"
namespace mini
{
	CRenderer::CRenderer(CDevice* pDevice)
	{
		mOrder = 0XFFFFFFFF;
		mRenderTypeFlag = RendererType::UnknowType;
		mpCamera = nullptr;
		mpDevice = pDevice;
		mpCurrentRenderNodes = &mRenderNodes;
		mpLightEffect = new CLightEffect;
		mpLightEffect->init(pDevice);
	}

	CRenderer::~CRenderer()
	{
		mpDevice = nullptr;
		SafeRelease(mpCamera);
		SafeRelease(mpLightEffect);
	}

	void CRenderer::render()
	{
		for (auto item : mRenderViewports)
		{
			item->apply();
		}
		_render();

	}

	void CRenderer::commitToGpu()
	{
		if (mAttchEffects.size() > 0)
		{
			for (auto item : mAttchEffects)
			{
				(item.second)->commitToGpu(mpDeferredContext);
			}
		}
		else
		{
			mRendererBoundBox.clear();
			for (auto& item : mZbufferOffRenderUnits)
			{
				auto _item = (item.second);
				auto& boundBox = _item.second->getBoundBox();
				if (boundBox.isShow())
				{
					mRendererBoundBox.push_back(&boundBox);
				}
				(_item.second)->render(mpDeferredContext);
			}

			for (auto& item : mRenderUnits)
			{
				for (auto& child : (item.second))
				{
					auto& boundBox = child->getBoundBox();
					if (boundBox.isShow())
					{
						mRendererBoundBox.push_back(&boundBox);
					}
					(item.first)->setShader(mpDeferredContext);
					(item.first)->apply(mpDeferredContext);
					child->render(mpDeferredContext);
				}
			}
			for (auto& item : mAlphaRenderUnits)
			{
				for (auto& child : (item.second))
				{
					auto& boundBox = child->getBoundBox();
					if (boundBox.isShow())
					{
						mRendererBoundBox.push_back(&boundBox);
					}
					(item.first)->setShader(mpDeferredContext);
					(item.first)->apply(mpDeferredContext);
					child->render(mpDeferredContext);
				}
			}
#ifdef  EDITOR_ACTIVE	
			if (nullptr != CBoundBox::gShaderUnitLine)
			{
				CBoundBox& box = mpCamera->getBoundBox();
				if (box.isShow())
				{
					mRendererBoundBox.push_back(&box);
				}
				auto matrices = CRendererEngine::getSinglePtr()->getTransposeMatrices();
				D3DXMatrixIdentity(&matrices[0]);
				CBoundBox::gShaderUnitLine->updateVSConstBuffer(0, &matrices[0]);
				CBoundBox::gShaderUnitLine->setShader(mpDeferredContext);
				CBoundBox::gShaderUnitLine->apply(mpDeferredContext);
			}
#endif
			for (auto item : mRendererBoundBox)
			{
				item->draw(mpDeferredContext);
			}
		}
	}

	void CRenderer::removeRenderNode(CRenderNode* pRenderNode)
	{
		auto item = std::find(mRenderNodes.begin(), mRenderNodes.end(), pRenderNode);
		if (item != mRenderNodes.end())
		{
			(*item)->Release();
			mRenderNodes.erase(item);
		}
	}

	void CRenderer::removeAllRenderNode()
	{
		for (auto item : mRenderNodes)
		{
			item->Release();
		}
		mRenderNodes.clear();
	}

	void CRenderer::updateLightToGpu()
	{
		mpLightEffect->clearAllActors();
		for( auto item : mLightActors )
		{
			mpLightEffect->addActor( item);
		}
		mpLightEffect->commitToGpu(mpDeferredContext);
	}

	void CRenderer::addUnit(CUnit* pUnit, CMaterialUnit* pMaterialUnit, EntityType Type, INT32 zLayer)
	{
		CSkinMeshUnit* pSkinMeshUnit = dynamic_cast<CSkinMeshUnit*>(pUnit);
		if (nullptr == pSkinMeshUnit)
		{
			CMeshUnit* pMeshUnit = dynamic_cast<CMeshUnit*>(pUnit);
			CMaterialUnit* pMaterilaUnit = pMeshUnit->getMaterialUnit();
			if (pMaterilaUnit != nullptr)
			{
				CShaderUnit* pShaderUnit = pMaterilaUnit->getShaderUnit(0);
				if (pShaderUnit != nullptr)
				{
					pMaterilaUnit->updateMaterial();
					pShaderUnit->setWorldMatrix(&pMeshUnit->getMatrix());
					pShaderUnit->setViewMatrix(&mpCamera->getViewMatrix());
					pShaderUnit->setProjectMatrix(&mpCamera->getProjectMatrix());
					if (pMaterilaUnit->isZbufferWrite())
					{
						if (pMaterilaUnit->isAlpha())
						{
							mAlphaRenderUnits[pShaderUnit].push_back(pMeshUnit);
						}
						else
						{
							mRenderUnits[pShaderUnit].push_back(pMeshUnit);
						}
					}
					else
					{
						mZbufferOffRenderUnits[zLayer] = std::make_pair(pShaderUnit, pMeshUnit);
					}
				}
			}
			for (auto item : mAttchEffects)
			{
				(item.second)->addUnit(pUnit, Type, zLayer);
			}
		}
	}

	void CRenderer::clearAllUnit()
	{
		mZbufferOffRenderUnits.clear();
		mRenderUnits.clear();
		mAlphaRenderUnits.clear();
	}

	const UINT32 CRenderer::getRendererOrder() const
	{
		return mOrder;
	}

	const RendererType CRenderer::getRendererType() const
	{
		return mRenderTypeFlag;
	}

	void CRenderer::setCamera(CCamera* pCamera)
	{
		if (nullptr != pCamera)
		{
			if (pCamera->isApplyRenderer(mRenderTypeFlag) && pCamera != mpCamera)
			{
				mpCamera = pCamera;
				mpCamera->AddRef();
			}
		}
		else
		{
			mpCamera = pCamera;
		}
	}

	CCamera* CRenderer::getCamera() const
	{
		return mpCamera;
	}

	void CRenderer::setDevice(CDevice* pDevice)
	{
		mpDevice = pDevice;
	}

	void CRenderer::setActive(bool bActive)
	{
		mbActive = bActive;
	}

	bool CRenderer::isActive()
	{
		return mbActive;
	}

	bool CRenderer::isEditor()
	{
		return mbEditor;
	}

	void CRenderer::applyRenderNodes(std::set<CRenderNode*>* pRenderNodes)
	{
		mpPreRenderNodes = mpCurrentRenderNodes;
		mpCurrentRenderNodes = pRenderNodes;
	}

	void CRenderer::applyPreRenderNodes()
	{
		if (nullptr != mpPreRenderNodes)
		{
			mpCurrentRenderNodes = mpPreRenderNodes;
			mpPreRenderNodes = nullptr;
		}
	}

	void CRenderer::executeRendererCommandList()
	{

	}

	void CRenderer::setDeviceContext(DeviceContext* pContext, int index )
	{
		mContextIndex = index;
		mpDeferredContext = pContext;
	}

	DeviceContext* CRenderer::getDeviceContext()
	{
		return mpDeferredContext;
	}

	int CRenderer::getDeviceContextIndex()
	{
		return mContextIndex;
	}

	void CRenderer::setEditor(bool bEditor)
	{
		mbEditor = bEditor;
	}

	void CRenderer::addRenderNode(CRenderNode* pRenderNode)
	{
		if (pRenderNode->getRendererFlag() & mRenderTypeFlag)
		{
			mRenderNodes.insert(pRenderNode);
		}
		auto pLight = dynamic_cast<CLightActor*>(pRenderNode->getActor());
		if (nullptr != pLight)
		{
			mLightActors.push_back(pLight);
		}
	}

	void CRenderer::addRenderViewPort(CRenderViewport* pViewPort)
	{
		mRenderViewports.push_back(pViewPort);
	}

	void CRenderer::removeRenderViewport(CRenderViewport* pViewport)
	{
		auto item = std::find(mRenderViewports.begin(), mRenderViewports.end(), pViewport);
		if (item != mRenderViewports.end())
		{
			mRenderViewports.erase(item);
		}
	}

	void CRenderer::processRenderNode(CRenderNode* pRenderNode)
	{
		auto pActor = pRenderNode->getActor();
		auto& units = pActor->getUnits();
		if (pRenderNode->mMaterialUnits.size() > 0)
		{
			auto  materialUnits = pRenderNode->mMaterialUnits.begin();
			for (auto child : units)
			{
				addUnit(child, *materialUnits, pActor->getEntityType(), pRenderNode->getRenderLayer());
				++materialUnits;
			}
		}
		else
		{
			for (auto child : units)
			{
				addUnit(child, nullptr, pActor->getEntityType(), pRenderNode->getRenderLayer());
			}
		}
	}

	void CRenderer::_render()
	{
		if (mpCamera != nullptr && nullptr != mpCurrentRenderNodes)
		{
			mpCamera->updateFrame(mpDeferredContext);
			updateLightToGpu();
			for (auto item : *mpCurrentRenderNodes)
			{
				auto pActor = item->getActor();
				if (nullptr != pActor &&
					pActor->isVisual() &&
					mpCamera->isInFrustum(pActor))
				{
					processRenderNode(item);
				}
			}
			commitToGpu();
			clearAllUnit();
		}
	}

	void CRenderer::addEffect(CEffect* pEffect, int order)
	{
		auto item = mAttchEffects.find(order);
		if (item != mAttchEffects.end())
		{
			if (pEffect != item->second)
			{
				SafeRelease((item->second));
			}
			else
			{
				return;
			}
		}
		mAttchEffects[order] = pEffect;
		pEffect->AddRef();
	}

	void CRenderer::removeEffect(CEffect* pEffect)
	{
		for( auto item : mAttchEffects )
		{
			if (item.second == pEffect)
			{
				SafeRelease(pEffect);
				mAttchEffects[item.first] = nullptr;
				break;
			}
		}
	}

}
